package com.upay.android.core.service;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.codehaus.jackson.type.TypeReference;

import android.content.AsyncTaskLoader;
import android.content.Context;

import com.upay.android.controller.helper.Keys;
import com.upay.android.domain.authentication.AuthenticationToken;
import com.upay.android.domain.request.ProtectedRequestHeader;
import com.upay.android.domain.request.RequestEnvelope;
import com.upay.android.domain.request.RequestHeader;
import com.upay.android.domain.response.ErrorDetail;
import com.upay.android.domain.response.ErrorType;
import com.upay.android.domain.response.ResponseEnvelope;
import com.upay.android.domain.response.ResponseError;
import com.upay.android.domain.rest.RestRequest;
import com.upay.android.util.exception.RestException;
import com.upay.android.util.rest.RestTemplate;

public class RestService<H extends RequestHeader, T extends Object, X extends Object> extends AsyncTaskLoader<ResponseEnvelope<X>> {
	
	/* ---- Instance Variables ---- */
	private String mURL = null;
	private RequestEnvelope<H,T> mRequestEnvelope = null;
	private TypeReference<ResponseEnvelope<X>> mTypeReference = null;
	private ResponseEnvelope<X> mResponseEnvelope = null;


	/* ---- Constructors ---- */
	
	RestService(Context context) {
		super(context);
	}

	public RestService(Context context, RestRequest<H,T,X> restRequest) {
		super(context);
		this.mURL = restRequest.getUrl();
		this.mRequestEnvelope = restRequest.getRequestEnvelope();
		this.mTypeReference = restRequest.getTypeReference();
	}

	/* ---- Business Methods ---- */
		
	@Override
	public ResponseEnvelope<X> loadInBackground() {
		ResponseEnvelope<X> responseEnvelope = null;
		try {
			RestTemplate<H,T,X> template = new RestTemplate<H,T,X>();
			responseEnvelope = template.postForEntity(this.mURL, this.mRequestEnvelope, this.mTypeReference, this.getHeaderParameters(this.mRequestEnvelope));
		} catch (RestException exception) {
			responseEnvelope.getResponseHeader().setStatus(Boolean.FALSE);
			ResponseError responseError = new ResponseError(ErrorType.PROGRAM_ERROR);
			responseError.addError(new ErrorDetail(
		        exception.getRestExceptionType().name(),
				exception.getMessage(),
				ErrorType.PROGRAM_ERROR
			));
			responseEnvelope.getResponseBody().setResponseError(responseError);
		}
		return responseEnvelope;
	}
	
	private List<NameValuePair> getHeaderParameters(RequestEnvelope<H,T> requestEnvelope) {
		List<NameValuePair> parameters = new ArrayList<NameValuePair>();
		
		if (requestEnvelope.getRequestHeader() instanceof ProtectedRequestHeader) {
			AuthenticationToken authenticationToken = ((ProtectedRequestHeader)requestEnvelope.getRequestHeader()).getAuthenticationToken();
			parameters.add(new BasicNameValuePair(Keys.USERID, authenticationToken.getUserName()));
			parameters.add(new BasicNameValuePair(Keys.TOKEN, authenticationToken.getToken()));
		}
		return parameters;
	}
	
	@Override
	protected void onStartLoading() {
		if (mResponseEnvelope != null) {
			deliverResult(mResponseEnvelope);
		} 
		
		if (takeContentChanged()  || mResponseEnvelope == null) {
            forceLoad();
        };
	}
	
	@Override
	public void deliverResult(ResponseEnvelope<X> responseEnvelope) {
		mResponseEnvelope = responseEnvelope;
		if (isStarted()) {
			super.deliverResult(responseEnvelope);
		}
	}
	
	@Override
	protected void onStopLoading() {
	    cancelLoad();
	}
	
	@Override
	protected void onReset() {
		super.onReset();
	    // Ensure the loader has been stopped.
	    onStopLoading();

	    // At this point we can release the resources associated with 'mData'.
	    if (mResponseEnvelope != null) {
	    	mResponseEnvelope = null;
	    }
	  }

	  @Override
	  public void onCanceled(ResponseEnvelope<X> responseEnvelope) {
	    // Attempt to cancel the current asynchronous load.
	    super.onCanceled(responseEnvelope);
	  }
	
	/* ---- Getters and Setters ---- */

	/**
	 * @return the url
	 */
	public String getUrl() {
		return mURL;
	}

	/**
	 * @param url the url to set
	 */
	public void setUrl(String url) {
		this.mURL = url;
	}

	/**
	 * @return the typeReference
	 */
	public TypeReference<ResponseEnvelope<X>> getTypeReference() {
		return mTypeReference;
	}

	/**
	 * @param typeReference the typeReference to set
	 */
	public void setTypeReference(TypeReference<ResponseEnvelope<X>> typeReference) {
		this.mTypeReference = typeReference;
	}

	/**
	 * @return the requestEnvelope
	 */
	public RequestEnvelope<H,T> getRequestEnvelope() {
		return mRequestEnvelope;
	}

	/**
	 * @param requestEnvelope the requestEnvelope to set
	 */
	public void setRequestEnvelope(RequestEnvelope<H,T> requestEnvelope) {
		this.mRequestEnvelope = requestEnvelope;
	}

	public ResponseEnvelope<X> getResponseEnvelope() {
		return mResponseEnvelope;
	}

	public void setResponseEnvelope(ResponseEnvelope<X> responseEnvelope) {
		this.mResponseEnvelope = responseEnvelope;
	}

}
